package main

import (
	"bufio"
	"bytes"
	"fmt"
	"strings"
)

func main() {
	/*
		5.type SplitFunc
			type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
				SplitFunc 类型代表用于对输出作 词法分析 的分割函数。
			（1）参数data：尚未处理的数据的一个开始部分的切片；即需要被分割的字节数组
				参数atEOF：表示是否 Reader 接口不能提供更多的数据，即是否已经到达输入流的结尾

			（2）返回值是 从 data 中已处理的字节数，将要返回给调用者的 token 切片（下一个要输出的分割子序列），以及可能遇到的错误。

			（3）如果数据不足以（保证）生成一个完整的 token，
				例如需要一行数据但是data里没有换行符，
				SplitFunc 可以返回 (0, nil, nil) 来告诉Scanner读取更多的数据写入切片
				然后用从同一位置起始、长度更长的切片再试一次（调用SplitFunc类型函数）。

			（4）如果返回值 err 非nil，扫描将终止并将该错误返回给 Scanner 的调用者。

			（5）除非 atEOF 为真，永远不会使用空切片 data 调用 SplitFunc 类型的函数。
				然而，如果 atEOF 为真，data 却可能是 非空的、且包含着未处理的文本。

			（6）SplitFunc 的作用很简单，从 data 中找出你感兴趣的数据，然后返回并告诉调用者，data 中有多少数据你已经处理过了。

		6.type Scanner
		（1）基础介绍：
			type Scanner struct {
				r            io.Reader // 客户提供的阅读器
				split        SplitFunc // 拆分 tokens 的函数
				maxTokenSize int       // Maximum size of a token; modified by tests.
				token        []byte    // Last token returned by split.
				buf          []byte    // Buffer used as argument to split.
				start        int       // First non-processed byte in buf.
				end          int       // End of data in buf.
				err          error     // Sticky error.
				empties      int       // Count of successive empty tokens.
				scanCalled   bool      // Scan has been called; buffer is in use.
				done         bool      // Scan has finished.
			}

			1.Scanner 类型提供了方便的读取数据的接口，
				如从换行符分割的文本里读取每一行。成功调用的 Scan方法 会逐步提供文件的token，跳过token之间的字节。

			2.token 由 SplitFunc 类型的分割函数指定；

			3.默认的分割函数会将输入分割为多个行，并去掉行尾的换行标志。
				本包预定义的分割函数可以将文件分割为 行、字节、Unicode码值、空白分割的word。
				调用者可以定制自己的分割函数。

			4.扫描会在 抵达输入流结尾、遇到的第一个 I/O 错误、token过大不能保存进缓冲时 不可恢复的停止。
				当扫描停止后，当前读取位置可能会在最后一个获得的 token 后面。

			5.需要更多对错误管理的控制或token很大，或必须从 reader 连续扫描的程序，应使用 bufio.Reader 代替。

		（2）方法：
			1.func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)
				ScanBytes 是用于 Scanner类型 的分割函数（符合SplitFunc）
				本函数会将每个字节作为一个 token 返回。

			2.func ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error)
				ScanRunes 是用于 Scanner类型 的分割函数（符合SplitFunc）
				本函数将每个 UTF-8 编码的 unicode码值 作为一个 token 返回。
				本函数返回的 rune 序列和 range 一个字符串的输出 rune 序列相同。

				错误的 UTF-8编码 会翻译为 U+FFFD = "\xef\xbf\xbd"，但只会消耗一个字节。
					调用者无法区分正确编码的 rune 和错误编码的 rune。

			3.func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error)
				ScanWords 是用于 Scanner类型 的分割函数（符合SplitFunc）
				本函数会将空白（参见 unicode.IsSpace）分割的片段（去掉前后空白后）作为一个 token 返回。
				本函数永远不会返回空字符串。
				用来找出 data 中的单行数据并返回（包括空行）

			4.func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error)
				ScanLines 是用于 Scanner类型 的分割函数（符合SplitFunc）
				本函数会将每一行文本去掉末尾的换行标记符作为一个 token 返回。
				返回的行可以是空字符串。
				换行标记为一个可选的回车后跟一个必选的换行符。
				最后一行即使没有换行符也会作为一个 token 返回。

			5.func NewScanner(r io.Reader) *Scanner
				NewScanner创建并返回一个从 r 读取数据的 Scanner，默认的分割函数是 ScanLines

			6.func (b *Scanner) Split(split SplitFunc)
				Split 设置该 Scanner 的分割函数。本方法必须在 Scan 之前调用。

			7.func (s *Scanner) Scan() bool
				Scan 方法获取当前位置的 token（该 token 可以通过 Bytes 或 Text 方法获得），并让 Scanner 的扫描位置移动到下一个 token。
				当扫描因为 抵达输入流结尾 或 遇到错误 而停止时，本方法会返回false。
				在 Scan 方法返回 false 后，Err方法 将返回扫描时遇到的任何错误；
					除非是 io.EOF，此时会返回 nil。

			8.func (s *Scanner) Bytes() []byte
				Bytes 方法返回最近一次 Scan 调用生成的 token。底层数组指向的数据可能会被下一次 Scan 的调用重写。

			9.func (s *Scanner) Text() string
				Text 方法返回最近一次 Scan 调用生成的 token，会申请创建一个字符串保存 token 并返回该字符串。
					所以，如果需要多次使用同一个 token。建议将其保存到一个变量中，而不是反复调用 Text() 方法

			10.func (s *Scanner) Err() error
				Err 返回 Scanner 遇到的第一个 非EOF 的错误。

	*/
	//type SplitFunc
	input := "Hello world, welcome to Go programing."
	scanner := bufio.NewScanner(bytes.NewBufferString(input))
	scanner.Split(splitBySpace)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
	/*结果：
	Hello
	world,
	welcome
	to
	Go
	programing.
	*/

	fmt.Println("----------------------------------------------")

	//type Scanner
	//Spit
	s := strings.NewReader("ABC DEF GHI IJL")
	bs := bufio.NewScanner(s)
	bs.Split(bufio.ScanWords)
	for bs.Scan() {
		fmt.Println(bs.Text())
	}
	/*结果：
	ABC
	DEF
	GHI
	IJL
	*/

	fmt.Println("------------------------------------------")

	//Scan
	s1 := strings.NewReader("Hello,喜羊羊！\n")
	bs1 := bufio.NewScanner(s1)
	bs1.Split(bufio.ScanBytes)
	for bs1.Scan() {
		fmt.Printf("%s", bs1.Text())
	}
	/*结果：
	Hello,喜羊羊！

	*/

	fmt.Println("-----------------------------------------------")

	//Bytes
	s2 := strings.NewReader("你好，喜羊羊！\n")
	bs2 := bufio.NewScanner(s2)
	bs2.Split(bufio.ScanRunes)
	for bs2.Scan() {
		fmt.Printf("%s", bs2.Bytes())
	}
	/*结果：
	"你好，喜羊羊！

	*/
}

func splitBySpace(data []byte, atEOF bool) (advance int, token []byte, err error) {
	//找到下一个空格的索引
	for i := 0; i < len(data); i++ {
		if data[i] == ' ' {
			return i + 1, data[:i], nil
		}
	}

	//如果遇到 EOF，并且 data 中还有未处理的字节，则返回剩余的数据作为最后一个标记
	if atEOF && len(data) > 0 {
		return len(data), data, nil
	}

	//请求更多数据
	return 0, nil, nil
}
